package com.svw.asmpcloud.handler;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.svw.asmpcloud.service.UserService;
import com.svw.asmpcloud.util.TokenUtils;

import reactor.core.publisher.Mono;

/**
 * 自定义的登录验证器<br>
 * 由于SpringGateWay基于WebFlux，所以SpringSecruity很多原有写法，都得改为WebFlux的方式才能生效！
 */
@Configuration // 此类会自动注入到SpringSecurity中，无需手动指定配置
public class CusReactiveAuthenticationManager implements ReactiveAuthenticationManager {

    // 解密用的（在SpringSecurityConfig中配置的bean）
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserService userService;

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {

        // 获取输入的用户名
        String username = authentication.getName();
        // 获取输入的明文
        String rawPassword = (String) authentication.getCredentials();

        UserDetails user = null;
        if ("jwttoken".equals(username)) {
            user = TokenUtils.validationToken(rawPassword);
            if (user == null) {
                return Mono.error(new CredentialsExpiredException("token验证失败，请重新登录"));
            }
        } else {
            try {
                user = userService.loadUserByUsername(username);
            } catch (UsernameNotFoundException ufe) {
                return Mono.error(ufe);
            }
            // 用户若不存在，则会在上面方法中抛出异常，所以能到这里用户一定存在
            if (!user.isEnabled()) {
                return Mono.error(new DisabledException("该账户已被禁用，请联系管理员"));

            } else if (!user.isAccountNonLocked()) {
                return Mono.error(new LockedException("该账号已被锁定"));
            } else if (!user.isAccountNonExpired()) {
                return Mono.error(new AccountExpiredException("该账号已过期，请联系管理员"));

            } else if (!user.isCredentialsNonExpired()) {
                return Mono.error(new CredentialsExpiredException("该账户的登录凭证已过期，请重新登录"));
            }

            // 验证密码
            if (!passwordEncoder.matches(rawPassword, user.getPassword())) {
                return Mono.error(new BadCredentialsException("用户不存在或者密码错误"));
            }
        }

        Authentication authen = new UsernamePasswordAuthenticationToken(user, rawPassword, user.getAuthorities());
        // WebFlux方式默认没有放到context中，需要手动放入
        SecurityContextHolder.getContext().setAuthentication(authen);

        return Mono.just(authen);
    }
}
